#include <hex/api/content_registry.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/ui/imgui_imhex_extensions.h>

#include <imgui.h>

namespace hex::plugin::builtin {
    namespace impl {
        // Tool for converting between different number formats.
        // There are three places where input can be changed; the bit checkboxes, the hex input, and the decimal input.
        // The bit checkboxes and the hex input are directly related and can be converted between each other easily.
        // The decimal input is a bit more complicated. IEEE 754 floating point numbers are represented as a sign bit,
        // an exponent and a mantissa. For details see https://en.wikipedia.org/wiki/IEEE_754.
        // Workflow is as follows:
        // From the bit checkboxes determine the integer hex value. This is straightforward.
        // From the hex value determine the binary floating point value by extracting the sign, exponent, and mantissa.
        // From the binary floating point value determine the decimal floating point value using a third party library.
        // From the decimal floating point we reconstruct the binary floating point value using internal hardware.
        // If the format is non-standard, the reconstruction is done using properties of the format.
        void drawIEEE754Decoder() {

            constexpr static auto flags = ImGuiInputTextFlags_EnterReturnsTrue;

            class IEEE754STATICS {
            public:
                IEEE754STATICS() {
                    value = 0;
                    exponentBitCount = 8;
                    mantissaBitCount = 23;
                    resultFloat = 0;
                }

                u128 value;
                i32 exponentBitCount;
                i32 mantissaBitCount;
                long double resultFloat;
            };

            static IEEE754STATICS ieee754statics;


            enum class NumberType {
                Normal,
                Zero,
                Denormal,
                Infinity,
                NaN,
            };

            enum class InputType {
                Infinity,
                NotANumber,
                QuietNotANumber,
                SignalingNotANumber,
                Regular,
                Invalid
            };

            enum class ValueType {
                Regular,
                SignalingNaN,
                QuietNaN,
                NegativeInfinity,
                PositiveInfinity,
            };

            class IEEE754 {
            public:
                ValueType valueType;
                NumberType numberType;
                i64 exponentBias;
                long double signValue;
                long double exponentValue;
                long double mantissaValue;
                i64 signBits;
                i64 exponentBits;
                i64 mantissaBits;
                i64 precision;
            } ieee754 = {};

            std::string specialNumbers[] = {
                    "inf" , "Inf", "INF" , "nan" , "Nan" , "NAN",
                    "qnan","Qnan", "QNAN", "snan", "Snan", "SNAN"
            };


            const auto totalBitCount = ieee754statics.exponentBitCount + ieee754statics.mantissaBitCount;
            const auto signBitPosition = totalBitCount - 0;
            const auto exponentBitPosition = totalBitCount - 1;
            const auto mantissaBitPosition = totalBitCount - 1 - ieee754statics.exponentBitCount;

            const static auto ExtractBits = [](u32 startBit, u32 count) {
                return hex::extract(startBit, startBit - (count - 1), ieee754statics.value);
            };

            ieee754.signBits = ExtractBits(signBitPosition, 1);
            ieee754.exponentBits = ExtractBits(exponentBitPosition, ieee754statics.exponentBitCount);
            ieee754.mantissaBits = ExtractBits(mantissaBitPosition, ieee754statics.mantissaBitCount);


            static i64 inputFieldWidth = 0;
            ImGuiExt::TextFormattedWrapped("{}", "hex.builtin.tools.ieee754.description"_lang);
            ImGui::NewLine();

            static i64 displayMode = ContentRegistry::Settings::read("hex.builtin.tools.ieee754.settings", "display_mode", 0);
            i64 displayModeTemp = displayMode;
            ImGui::RadioButton("hex.builtin.tools.ieee754.settings.display_mode.detailed"_lang, reinterpret_cast<int *>(&displayMode), 0);
            ImGui::SameLine();

            ImGui::RadioButton("hex.builtin.tools.ieee754.settings.display_mode.simplified"_lang, reinterpret_cast<int *>(&displayMode), 1);
            if (displayModeTemp != displayMode) {
                ContentRegistry::Settings::write("hex.builtin.tools.ieee754.settings", "display_mode", displayMode);
                displayModeTemp = displayMode;
            }

            auto tableFlags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoKeepColumnsVisible |
                                     ImGuiTableFlags_ScrollX | ImGuiTableFlags_NoPadInnerX;

            const static auto IndentBoxOrLabel = [](u32 startBit, u32 bitIndex, u32 count, bool isLabel) {
                auto checkBoxWidth = ImGui::CalcTextSize("0").x + ImGui::GetStyle().FramePadding.x * 2.0f;
                auto columnWidth = ImGui::GetColumnWidth();
                float boxesPerColumn=columnWidth/checkBoxWidth;
                float result;
                if (isLabel) {
                    std::string labelString = fmt::format("{}", bitIndex);
                    auto labelWidth = ImGui::CalcTextSize(labelString.c_str()).x;
                    auto leadingBoxes = (boxesPerColumn-count)/2.0f;
                    if (leadingBoxes < 0.0f)
                       leadingBoxes = 0.0f;
                    result = checkBoxWidth*(leadingBoxes + startBit - bitIndex + 0.5f)-labelWidth/2.0f;
                } else {
                    if (count < boxesPerColumn)
                        result = (columnWidth - count * checkBoxWidth) / 2.0f;
                    else
                        result = 0.0;
                }
                if (result <= 0.0f)
                    result = 0.05;
                return result;
            };

            const static auto DisplayBitLabels = [](int startBit, int count) {
                static i32 lastLabelAdded = -1;
                i32 labelIndex;
                if (lastLabelAdded == -1 || count < 4)
                    labelIndex = startBit - (count >> 1);
                else
                    labelIndex = lastLabelAdded - 4;

                while (labelIndex + count > startBit) {
                    auto indentSize = IndentBoxOrLabel(startBit, labelIndex, count, true);
                    ImGui::Indent(indentSize );
                    ImGuiExt::TextFormatted("{}", labelIndex);
                    lastLabelAdded = labelIndex;
                    ImGui::Unindent(indentSize );
                    labelIndex -= 4;
                    ImGui::SameLine();
                }
            };

            const static auto FormatBitLabels = [](i32 totalBitCount, i32 exponentBitPosition, i32 mantissaBitPosition) {
                // Row for bit labels. Due to font size constrains each bit cannot have its own label.
                // Instead, we label each 4 bits and then use the bit position to determine the bit label.
                // Result.
                ImGui::TableNextColumn();
                // Equals.
                ImGui::TableNextColumn();
                // Sign bit label is always shown.
                ImGui::TableNextColumn();

                DisplayBitLabels(totalBitCount + 1, 1);
                // Times.
                ImGui::TableNextColumn();
                // Exponent.
                ImGui::TableNextColumn();

                DisplayBitLabels(exponentBitPosition + 1, ieee754statics.exponentBitCount);

                // Times.
                ImGui::TableNextColumn();
                // Mantissa.
                ImGui::TableNextColumn();

                DisplayBitLabels(mantissaBitPosition + 1, ieee754statics.mantissaBitCount);
            };



            const static auto BitCheckbox = [](u8 bit) {
                bool checkbox = false;
                ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
                checkbox = (ieee754statics.value & (u128(1) << bit)) != 0;
                ImGuiExt::BitCheckbox("##checkbox", &checkbox);
                ieee754statics.value = (ieee754statics.value & ~(u128(1) << bit)) | (u128(checkbox) << bit);
                ImGui::PopStyleVar();
            };


            const static auto BitCheckboxes = [](u32 startBit, u32 count) {
                for (u32 i = 0; i < count; i++) {
                    ImGui::PushID(startBit - i);
                    BitCheckbox(startBit - i);
                    ImGui::SameLine(0, 0);
                    ImGui::PopID();
                }
            };

            const static auto FormatBits = [](i32 signBitPosition, i32 exponentBitPosition, i32 mantissaBitPosition) {

                // Sign.
                ImGui::TableNextColumn();

                ImVec4 signColor = ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_IEEEToolSign);
                ImVec4 expColor = ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_IEEEToolExp);
                ImVec4 mantColor = ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_IEEEToolMantissa);
                ImVec4 black = ImVec4(0.0, 0.0, 0.0, 1.0);

                float indent = IndentBoxOrLabel(signBitPosition,signBitPosition, 1, false);
                ImGui::Indent(indent);
                ImGui::PushStyleColor(ImGuiCol_FrameBg, signColor);
                ImGui::PushStyleColor(ImGuiCol_Border, black);

                BitCheckboxes(signBitPosition, 1);

                ImGui::PopStyleColor();
                ImGui::PopStyleColor();
                ImGui::Unindent(indent);

                // Times.
                ImGui::TableNextColumn();
                // Exponent.
                ImGui::TableNextColumn();
                indent = IndentBoxOrLabel(exponentBitPosition,exponentBitPosition, ieee754statics.exponentBitCount, false);
                ImGui::Indent(indent);
                ImGui::PushStyleColor(ImGuiCol_FrameBg, expColor);
                ImGui::PushStyleColor(ImGuiCol_Border, black);

                BitCheckboxes(exponentBitPosition, ieee754statics.exponentBitCount);

                ImGui::PopStyleColor();
                ImGui::PopStyleColor();
                ImGui::Unindent(indent);
                // Times.
                ImGui::TableNextColumn();
                // Mantissa.
                ImGui::TableNextColumn();

                indent = IndentBoxOrLabel(mantissaBitPosition,mantissaBitPosition, ieee754statics.mantissaBitCount, false);
                ImGui::Indent(indent);
                ImGui::PushStyleColor(ImGuiCol_FrameBg, mantColor);
                ImGui::PushStyleColor(ImGuiCol_Border, black);

                BitCheckboxes(mantissaBitPosition, ieee754statics.mantissaBitCount);

                ImGui::PopStyleColor();
                ImGui::PopStyleColor();
                ImGui::Unindent(indent);
            };

            const static auto BitsToFloat = [](IEEE754 &ieee754) {
                // Zero or denormal.
                if (ieee754.exponentBits == 0) {
                    // Result doesn't fit in 128 bits.
                    if ((ieee754.exponentBias - 1) > 128)
                        ieee754.exponentValue = std::pow(2.0L, static_cast<long double>(-ieee754.exponentBias + 1));
                    else {
                        if (ieee754.exponentBias == 0) {
                            // Exponent is zero.
                            if (ieee754.mantissaBits == 0)
                                ieee754.exponentValue = 1.0;
                            else
                                // Exponent is one.
                                ieee754.exponentValue = 2.0;
                        }
                        else
                            ieee754.exponentValue = 1.0 / static_cast<long double>(u128(1) << (ieee754.exponentBias - 1));
                    }
                }
                    // Normal.
                else {
                    // Result doesn't fit in 128 bits.
                    if (std::abs(ieee754.exponentBits - ieee754.exponentBias) > 128)
                        ieee754.exponentValue = std::pow(2.0L, static_cast<long double>(ieee754.exponentBits - ieee754.exponentBias));
                        //Result fits in 128 bits.
                    else {
                        // Exponent is positive.
                        if (ieee754.exponentBits > ieee754.exponentBias)
                            ieee754.exponentValue = static_cast<long double>(u128(1) << (ieee754.exponentBits - ieee754.exponentBias));
                            // Exponent is negative.
                        else if (ieee754.exponentBits < ieee754.exponentBias)
                            ieee754.exponentValue = 1.0 / static_cast<long double>(u128(1) << (ieee754.exponentBias - ieee754.exponentBits));
                            // Exponent is zero.
                        else ieee754.exponentValue = 1.0;
                    }
                }

                ieee754.mantissaValue = static_cast<long double>(ieee754.mantissaBits) / static_cast<long double>(u128(1) << (ieee754statics.mantissaBitCount));
                if (ieee754.exponentBits != 0)
                    ieee754.mantissaValue += 1.0;

                // Check if all exponent bits are set.
                if (std::popcount(static_cast<u64>(ieee754.exponentBits)) == static_cast<i64>(ieee754statics.exponentBitCount)) {
                    // If fraction is zero number is infinity,
                    if (ieee754.mantissaBits == 0) {
                        if (ieee754.signBits == 0) {

                            ieee754.valueType = ValueType::PositiveInfinity;
                            ieee754statics.resultFloat = std::numeric_limits<long double>::infinity();

                        } else {

                            ieee754.valueType = ValueType::NegativeInfinity;
                            ieee754statics.resultFloat = -std::numeric_limits<long double>::infinity();

                        }
                        ieee754.numberType = NumberType::Infinity;

                        // otherwise number is NaN.
                    } else {
                        if (ieee754.mantissaBits & (u128(1) << (ieee754statics.mantissaBitCount - 1))) {

                            ieee754.valueType = ValueType::QuietNaN;
                            ieee754statics.resultFloat = std::numeric_limits<long double>::quiet_NaN();

                        } else {

                            ieee754.valueType = ValueType::SignalingNaN;
                            ieee754statics.resultFloat = std::numeric_limits<long double>::signaling_NaN();

                        }
                        ieee754.numberType = NumberType::NaN;
                    }
                    // If all exponent bits are zero, but we have a non-zero fraction
                    // then the number is denormal which are smaller than regular numbers
                    // but not as precise.
                } else if (ieee754.exponentBits == 0 && ieee754.mantissaBits != 0) {

                    ieee754.numberType = NumberType::Denormal;
                    ieee754.valueType = ValueType::Regular;
                    ieee754statics.resultFloat = ieee754.signValue * ieee754.exponentValue * ieee754.mantissaValue;

                } else {

                    ieee754.numberType = NumberType::Normal;
                    ieee754.valueType = ValueType::Regular;
                    ieee754statics.resultFloat = ieee754.signValue * ieee754.exponentValue *  ieee754.mantissaValue;

                }
            };

            const static auto FloatToBits = [&specialNumbers](IEEE754 &ieee754, std::string decimalFloatingPointNumberString, int totalBitCount) {

                // Always obtain sign first.
                if (decimalFloatingPointNumberString[0] == '-') {
                    // And remove it from the string.
                    ieee754.signBits = 1;
                    decimalFloatingPointNumberString.erase(0, 1);
                } else
                    // Important to switch from - to +.
                    ieee754.signBits = 0;

                InputType inputType = InputType::Regular;
                bool matchFound = false;

                // Detect and use special numbers.
                for (u32 i = 0; i < 12; i++) {
                    if (decimalFloatingPointNumberString == specialNumbers[i]) {
                        inputType = InputType(i/3);
                        matchFound = true;
                        break;
                    }
                }

                if (!matchFound)
                    inputType = InputType::Regular;

                if (inputType == InputType::Regular) {
                    try {
                        ieee754statics.resultFloat = stod(decimalFloatingPointNumberString);
                    } catch(const std::invalid_argument& _) {
                        inputType = InputType::Invalid;
                    }
                } else if (inputType == InputType::Infinity) {
                    ieee754statics.resultFloat = std::numeric_limits<long double>::infinity();
                    ieee754statics.resultFloat *= (ieee754.signBits == 1 ? -1 : 1);

                } else if (inputType == InputType::NotANumber)
                    ieee754statics.resultFloat = std::numeric_limits<long double>::quiet_NaN();

                else if (inputType == InputType::QuietNotANumber)
                    ieee754statics.resultFloat = std::numeric_limits<long double>::quiet_NaN();

                else if (inputType == InputType::SignalingNotANumber)
                    ieee754statics.resultFloat = std::numeric_limits<long double>::signaling_NaN();


                if (inputType != InputType::Invalid) {
                    // Deal with zero first so we can use log2.
                    if (ieee754statics.resultFloat == 0.0) {
                        if (ieee754.signBits == 1)
                            ieee754statics.resultFloat = -0.0;
                        else
                            ieee754statics.resultFloat = 0.0;
                        ieee754.numberType = NumberType::Zero;
                        ieee754.valueType = ValueType::Regular;
                        ieee754.exponentBits = 0;
                        ieee754.mantissaBits = 0;

                    } else {
                        long double log2Result = std::log2(ieee754statics.resultFloat);
                        // 2^(bias+1)-2^(bias-prec) is the largest number that can be represented.
                        // If the number entered is larger than this then the input is set to infinity.
                        if (ieee754statics.resultFloat > (std::pow(2.0L, ieee754.exponentBias + 1) - std::pow(2.0L, ieee754.exponentBias - ieee754statics.mantissaBitCount)) || inputType == InputType::Infinity ) {

                            ieee754statics.resultFloat = std::numeric_limits<long double>::infinity();
                            ieee754.numberType = NumberType::Infinity;
                            ieee754.valueType = ieee754.signBits == 1 ? ValueType::NegativeInfinity : ValueType::PositiveInfinity;
                            ieee754.exponentBits = (u128(1) << ieee754statics.exponentBitCount) - 1;
                            ieee754.mantissaBits = 0;

                        } else if (-std::rint(log2Result) > ieee754.exponentBias + ieee754statics.mantissaBitCount - 1) {

                            // 1/2^(bias-1+prec) is the smallest number that can be represented.
                            // If the number entered is smaller than this then the input is set to zero.
                            if (ieee754.signBits == 1)
                                ieee754statics.resultFloat = -0.0;
                            else
                                ieee754statics.resultFloat = 0.0;

                            ieee754.numberType = NumberType::Zero;
                            ieee754.valueType = ValueType::Regular;
                            ieee754.exponentBits = 0;
                            ieee754.mantissaBits = 0;

                        } else if (inputType == InputType::SignalingNotANumber) {

                            ieee754statics.resultFloat = std::numeric_limits<long double>::signaling_NaN();
                            ieee754.valueType = ValueType::SignalingNaN;
                            ieee754.numberType = NumberType::NaN;
                            ieee754.exponentBits = (u128(1) << ieee754statics.exponentBitCount) - 1;
                            ieee754.mantissaBits = 1;

                        } else if (inputType == InputType::QuietNotANumber || inputType == InputType::NotANumber ) {

                            ieee754statics.resultFloat = std::numeric_limits<long double>::quiet_NaN();
                            ieee754.valueType = ValueType::QuietNaN;
                            ieee754.numberType = NumberType::NaN;
                            ieee754.exponentBits = (u128(1) << ieee754statics.exponentBitCount) - 1;
                            ieee754.mantissaBits = (u128(1) << (ieee754statics.mantissaBitCount - 1));

                        } else if (static_cast<i64>(std::floor(log2Result)) + ieee754.exponentBias <= 0) {

                            ieee754.numberType = NumberType::Denormal;
                            ieee754.valueType = ValueType::Regular;
                            ieee754.exponentBits = 0;
                            auto mantissaExp = log2Result + ieee754.exponentBias + ieee754statics.mantissaBitCount - 1;
                            ieee754.mantissaBits = static_cast<i64>(std::round(std::pow(2.0L, mantissaExp)));

                        } else {

                            ieee754.valueType = ValueType::Regular;
                            ieee754.numberType = NumberType::Normal;
                            i64 unBiasedExponent = static_cast<i64>(std::floor(log2Result));
                            ieee754.exponentBits = unBiasedExponent + ieee754.exponentBias;
                            ieee754.mantissaValue = ieee754statics.resultFloat * std::pow(2.0L, -unBiasedExponent) - 1;
                            ieee754.mantissaBits = static_cast<i64>(std::round( static_cast<long double>(u128(1) << (ieee754statics.mantissaBitCount)) * ieee754.mantissaValue));

                        }
                    }
                    // Put the bits together.
                    ieee754statics.value = (ieee754.signBits << (totalBitCount)) | (ieee754.exponentBits << (totalBitCount - ieee754statics.exponentBitCount)) | ieee754.mantissaBits;
                }
            };

            const static auto DisplayDecimal  = [](IEEE754 &ieee754) {

                unsigned signColorU32 = ImGuiExt::GetCustomColorU32(ImGuiCustomCol_IEEEToolSign);
                unsigned expColorU32 = ImGuiExt::GetCustomColorU32(ImGuiCustomCol_IEEEToolExp);
                unsigned mantColorU32 = ImGuiExt::GetCustomColorU32(ImGuiCustomCol_IEEEToolMantissa);

                ImGui::TableNextColumn();

                ImGui::Text("=");

                // Sign.
                ImGui::TableNextColumn();

                // This has the effect of dimming the color of the numbers so user doesn't try
                // to interact with them.
                ImVec4 textColor =  ImGui::GetStyleColorVec4(ImGuiCol_Text);
                ImGui::BeginDisabled();
                ImGui::PushStyleColor(ImGuiCol_Text, textColor);

                ImGui::Indent(10_scaled);
                ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, signColorU32);
                if (ieee754.signBits == 1)
                    ImGui::Text("-1");
                else
                    ImGui::Text("+1");
                ImGui::Unindent(10_scaled);

                // Times.
                ImGui::TableNextColumn();
                ImGui::Text("x");
                ImGui::TableNextColumn();

                // Exponent.
                ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, expColorU32);

                ImGui::Indent(20_scaled);
                if (ieee754.numberType == NumberType::NaN) {
                    if (ieee754.valueType == ValueType::QuietNaN)
                        ImGui::Text("qNaN");
                    else
                        ImGui::Text("sNaN");

                } else if (ieee754.numberType == NumberType::Infinity)
                    ImGui::Text("Inf");
                else if (ieee754.numberType == NumberType::Zero)
                    ImGui::Text("0");
                else if (ieee754.numberType == NumberType::Denormal)
                    ImGuiExt::TextFormatted("2^{0}", 1 - ieee754.exponentBias);
                else
                    ImGuiExt::TextFormatted("2^{0}", ieee754.exponentBits - ieee754.exponentBias);

                ImGui::Unindent(20_scaled);

                // Times.
                ImGui::TableNextColumn();
                ImGui::Text("x");
                ImGui::TableNextColumn();

                // Mantissa.
                ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, mantColorU32);
                ImGui::Indent(20_scaled);
                ImGuiExt::TextFormatted("{:.{}}", ieee754.mantissaValue,ieee754.precision);
                ImGui::Unindent(20_scaled);

                ImGui::PopStyleColor();
                ImGui::EndDisabled();
            };


            const static auto ToolMenu = [](i64 &inputFieldWidth) {
                // If precision and exponent match one of the IEEE 754 formats the format is highlighted
                // and remains highlighted until user changes to a different format. Matching formats occur when
                // the user clicks on one of the selections or if the slider values match the format in question.
                // When a new format is selected, it may have a smaller number of digits than
                // the previous selection. Since the largest of the hexadecimal and the decimal
                // representation widths set both field widths to the same value, we need to
                // reset it here when a new choice is set.

                auto exponentBitCount = ieee754statics.exponentBitCount;
                auto mantissaBitCount = ieee754statics.mantissaBitCount;
                if (ImGui::SliderInt("hex.builtin.tools.ieee754.exponent_size"_lang, &exponentBitCount, 1, 63 - mantissaBitCount)) {
                    inputFieldWidth = 0;
                    ieee754statics.exponentBitCount = exponentBitCount;
                }
                if (ImGui::SliderInt("hex.builtin.tools.ieee754.mantissa_size"_lang, &mantissaBitCount, 1, 63 - exponentBitCount)) {
                    inputFieldWidth = 0;
                    ieee754statics.mantissaBitCount = mantissaBitCount;
                }
                ImGui::Separator();

                auto color = ImGui::GetColorU32(ImGuiCol_ButtonActive);

                bool needsPop = false;
                if (ieee754statics.exponentBitCount == 5 && ieee754statics.mantissaBitCount == 10) {
                    ImGui::PushStyleColor(ImGuiCol_Button, color);
                    needsPop = true;
                }
                if (ImGui::Button("hex.builtin.tools.ieee754.half_precision"_lang)) {
                    ieee754statics.exponentBitCount = 5;
                    ieee754statics.mantissaBitCount = 10;
                    inputFieldWidth = 0;
                }
                if (needsPop) ImGui::PopStyleColor();

                ImGui::SameLine();

                needsPop = false;
                if (ieee754statics.exponentBitCount == 8 && ieee754statics.mantissaBitCount == 23) {
                    ImGui::PushStyleColor(ImGuiCol_Button, color);
                    needsPop = true;
                }
                if (ImGui::Button("hex.builtin.tools.ieee754.single_precision"_lang)) {
                    ieee754statics.exponentBitCount = 8;
                    ieee754statics.mantissaBitCount = 23;
                    inputFieldWidth = 0;
                }
                if (needsPop) ImGui::PopStyleColor();

                ImGui::SameLine();

                needsPop = false;
                if (ieee754statics.exponentBitCount == 11 && ieee754statics.mantissaBitCount == 52) {
                    ImGui::PushStyleColor(ImGuiCol_Button, color);
                    needsPop = true;
                }
                if (ImGui::Button("hex.builtin.tools.ieee754.double_precision"_lang)) {
                    ieee754statics.exponentBitCount = 11;
                    ieee754statics.mantissaBitCount = 52;
                    inputFieldWidth = 0;
                }
                if (needsPop) ImGui::PopStyleColor();

                ImGui::SameLine();

                needsPop = false;
                if (ImGui::Button("hex.builtin.tools.ieee754.clear"_lang))
                    // This will reset all interactive widgets to zero.
                    ieee754statics.value = 0;

                ImGui::Separator();

                ImGui::NewLine();
            };

            if (ImGui::BeginTable("##outer", 7, tableFlags, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5.5 ))) {
                ImGui::TableSetupColumn("hex.builtin.tools.ieee754.result.title"_lang);
                ImGui::TableSetupColumn("##equals");
                ImGui::TableSetupColumn("hex.builtin.tools.ieee754.sign"_lang);
                ImGui::TableSetupColumn("##times");
                ImGui::TableSetupColumn("hex.builtin.tools.ieee754.exponent"_lang);
                ImGui::TableSetupColumn("##times");
                ImGui::TableSetupColumn("hex.builtin.tools.ieee754.mantissa"_lang);
                ImGui::TableHeadersRow();
                ImGui::TableNextRow();

                FormatBitLabels(totalBitCount, exponentBitPosition, mantissaBitPosition);


                ImGui::TableNextRow();
                // Row for bit checkboxes
                // Result.
                ImGui::TableNextColumn();

                u64 mask = hex::bitmask(totalBitCount+1);
                std::string maskString = hex::format("0x{:X}  ", mask);

                auto style = ImGui::GetStyle();
                inputFieldWidth = std::fmax(inputFieldWidth,
                                            ImGui::CalcTextSize(maskString.c_str()).x + style.FramePadding.x * 2.0f);
                ImGui::PushItemWidth(inputFieldWidth);

                u64 newValue = ieee754statics.value & mask;
                if (ImGuiExt::InputHexadecimal("##hex", &newValue, flags))
                    ieee754statics.value = newValue;
                ImGui::PopItemWidth();

                // Equals.
                ImGui::TableNextColumn();
                ImGui::Text("=");

                FormatBits(signBitPosition, exponentBitPosition, mantissaBitPosition);


                ImGui::TableNextRow();
                ImGui::TableNextColumn();

                ieee754.exponentBias = (u128(1) << (ieee754statics.exponentBitCount - 1)) - 1;

                ieee754.signValue = ieee754.signBits == 0 ? 1.0 : -1.0;

                BitsToFloat(ieee754);

                if (ieee754.numberType == NumberType::Denormal)
                    ieee754.precision = std::ceil(1+ieee754statics.mantissaBitCount * std::log10(2.0L));
                else
                    ieee754.precision = std::ceil(1+(ieee754statics.mantissaBitCount + 1) * std::log10(2.0L));

                // For C++ from_chars is better than strtold.
                // The main problem is that from_chars will not process special numbers
                // like inf and nan, so we handle them manually.
                static std::string decimalFloatingPointNumberString;

                // Use qnan for quiet NaN and snan for signaling NaN.
                if (ieee754.numberType == NumberType::NaN) {
                    if (ieee754.valueType == ValueType::QuietNaN)
                        decimalFloatingPointNumberString = "qnan";
                    else
                        decimalFloatingPointNumberString = "snan";
                } else
                    decimalFloatingPointNumberString = fmt::format("{:.{}}", ieee754statics.resultFloat, ieee754.precision);

                auto style1 = ImGui::GetStyle();
                inputFieldWidth = std::fmax(inputFieldWidth, ImGui::CalcTextSize(decimalFloatingPointNumberString.c_str()).x + 2 * style1.FramePadding.x);
                ImGui::PushItemWidth(inputFieldWidth);


                // We allow any input in order to accept infinities and NaNs, all invalid entries
                // are detected catching exceptions. You can also enter -0 or -inf.
                if (ImGui::InputText("##resultFloat", decimalFloatingPointNumberString, flags)) {
                    FloatToBits(ieee754, decimalFloatingPointNumberString, totalBitCount);
                }
                ImGui::PopItemWidth();

                if (displayMode == 0)
                    DisplayDecimal(ieee754);

                ImGui::EndTable();

            }

            ToolMenu(inputFieldWidth);
        }
    }
}
